package demo.java.util.concurrent.locks;

import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import demo.java.lang.ThreadDemo;
import demo.java.util.concurrent.ThreadFactoryDemo;

/**
 * java 版本的数据库行锁,使用wait/notify实现，当然可以使用别的方式如Lock下的await/signal
 * 
 * @author hanjy
 *
 */
public class JavaRowLock<T> {

    private static Logger logger = LoggerFactory.getLogger(JavaRowLock.class);

    private ConcurrentHashMap<T, Object> lockedMap = new ConcurrentHashMap<>(256);

    private static final Object O = new Object();

    public static final Random R = new Random();

    public void lock(T id, int timeout) throws InterruptedException {
        long t1 = System.currentTimeMillis();
        while (lockedMap.putIfAbsent(id, O) != null) {
            ThreadDemo.safeSleep(1);
            logger.info("putIfAbsent {} failed,try again", id);
            if (System.currentTimeMillis() - t1 > timeout) {
                logger.info("putIfAbsent {} timeout", id);
                break;
            }
        }
        logger.info("putIfAbsent {} success", id);
    }

    public void unLock(T id) {
        Object o = lockedMap.remove(id);
        logger.info("remove {} return {}", id, o);
    }

    public static void main(String[] args) {
        int processorCount = Runtime.getRuntime().availableProcessors();
        logger.info("the number of processors available to the Java virtual machine is {}", processorCount);
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(processorCount, processorCount, 0L,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),
                new ThreadFactoryDemo.NamedThreadFactory("FixedExecutor"));

        JavaRowLock<String> lock = new JavaRowLock<>();

        for (int i = 0; i < 100; i++) {
            String key = "lock-" + R.nextInt(8);
            threadPool.submit(() -> {
                try {
                    lock.lock(key, 3);
                    ThreadDemo.safeSleep(20);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unLock(key);
                }
            });
        }
        // ThreadDemo.safeSleep(2000);
        threadPool.shutdown();
    }
}
